package com.example.logisticsingle.requsest;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.example.logisticsingle.String.StringUtils;

import com.example.logisticsingle.requsest.IPDetail;
import com.example.logisticsingle.requsest.ClinetRequestInfo;
import com.example.logisticsingle.utils.JsonUtils;
import eu.bitwalker.useragentutils.DeviceType;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 请求工具类
 *
 */
@Slf4j
public class RequestUtils {
    /**
     * 读取Request流信息
     * @param request
     * @return
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> getParamsFormRequest(final HttpServletRequest request) throws IOException{

        Map<String, Object> params = new HashMap<>();
        Enumeration<String> pNames = request.getParameterNames();

        // 封装请求参数
        switch (request.getMethod().toUpperCase()) {
            case "POST":
                RequestWrapper requestWrap = new RequestWrapper(request);
                if(requestWrap.getBody()!=null && requestWrap.getBody().length()>0) {
                    ObjectMapper mapper = new ObjectMapper();
                    params=mapper.readValue(requestWrap.getBody(), Map.class);
                    params.put("request", requestWrap);
                }
                while (pNames.hasMoreElements()) {
                    String pName = pNames.nextElement();
                    params.put(pName, request.getParameter(pName));
                }
                break;
            case "GET":
                while (pNames.hasMoreElements()) {
                    String pName = pNames.nextElement();
                    params.put(pName, request.getParameter(pName));
                }
                break;
            default:
                break;
        }
        return params;
    }
    /**
     * 获取请求IP地址
     * @param request
     * @return
     */
    public static String getIPAddress(HttpServletRequest request) {
        String ip = null;

        //X-Forwarded-For：Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP：apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP：weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP：有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP：nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }

        //有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        //还是不能获取到，最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }
    /**
     * 获取IP详细信息
     * @param request
     * @return
     */
    public static IPDetail getIPDetailInfo(HttpServletRequest request) {
        String ip = getIPAddress(request);
        return getIpDetailInfo(ip);
    }
    /**
     * 根据IP获取地址信息
     * @param ip
     * @return
     */
    public static IPDetail getIpDetailInfo(String ip) {
        if(StringUtils.isNull(ip)) return null;
        byte[] addr = textToNumericFormatV4(ip);
        IPDetail ipDetail = null;
        if("127.0.0.1".equals(ip)||internalIp(addr)) {
            ipDetail = new IPDetail();
            ipDetail.setIp(ip);
            ipDetail.setDesc("内网");
            return ipDetail;
        }else {
            ipDetail = getJsonContentForIPAddress("http://ip.taobao.com/service/getIpInfo.php?ip="+ip);
            if(ipDetail!=null) {
                ipDetail.setDesc(String.format("%s %s %s",ipDetail.getCountry(),ipDetail.getRegion(),ipDetail.getCity()));
            }else {
                ipDetail = new IPDetail();
                ipDetail.setIp(ip);
                ipDetail.setDesc("获取地理位置异常");
            }
        }
        return ipDetail;
    }
    /**
     * 获取来访者的浏览器信息
     * @param request
     * @return
     */
    public static String getBrowseInfo(HttpServletRequest request) {
        String header = request.getHeader("user-agent");
        return getBrowseInfo(header);
    }
    /**
     * 获取来访者的浏览器信息
     * @param agent
     * @return
     */
    public static String getBrowseInfo(String agent) {
        if(StringUtils.isNull(agent)){
            return "";
        }
        String browserVersion = null;
        if(agent.indexOf("MSIE")>0){
            browserVersion = "IE";
        }else if(agent.indexOf("Firefox")>0){
            browserVersion = "Firefox";
        }else if(agent.indexOf("Chrome")>0){
            browserVersion = "Chrome";
        }else if(agent.indexOf("Safari")>0){
            browserVersion = "Safari";
        }else if(agent.indexOf("Camino")>0){
            browserVersion = "Camino";
        }else if(agent.indexOf("Konqueror")>0){
            browserVersion = "Konqueror";
        }
        return browserVersion;
    }
    /**
     * 获取用户系统信息
     * @param request
     * @return
     */
    public static String getRequestSystemInfo(HttpServletRequest request){
        String header = request.getHeader("user-agent");
        if(header == null || header.equals("")){
            return "";
        }
        return getRequestSystemInfo(header);

    }
    /**
     * 获取用户系统信息
     * @param agent
     * @return
     */
    public static String getRequestSystemInfo(String agent){
        String systenInfo = null;
        //得到用户的操作系统
        if (agent.indexOf("NT 6.0") > 0){
            systenInfo = "Windows Vista/Server 2008";
        } else if (agent.indexOf("NT 5.2") > 0){
            systenInfo = "Windows Server 2003";
        } else if (agent.indexOf("NT 5.1") > 0){
            systenInfo = "Windows XP";
        } else if (agent.indexOf("NT 6.0") > 0){
            systenInfo = "Windows Vista";
        } else if (agent.indexOf("NT 6.1") > 0){
            systenInfo = "Windows 7";
        } else if (agent.indexOf("NT 6.2") > 0){
            systenInfo = "Windows Slate";
        } else if (agent.indexOf("NT 6.3") > 0){
            systenInfo = "Windows 9";
        } else if (agent.indexOf("NT 5") > 0){
            systenInfo = "Windows 2000";
        } else if (agent.indexOf("NT 4") > 0){
            systenInfo = "Windows NT4";
        } else if (agent.indexOf("Me") > 0){
            systenInfo = "Windows Me";
        } else if (agent.indexOf("98") > 0){
            systenInfo = "Windows 98";
        } else if (agent.indexOf("95") > 0){
            systenInfo = "Windows 95";
        } else if (agent.indexOf("Mac") > 0){
            systenInfo = "Mac";
        } else if (agent.indexOf("Unix") > 0){
            systenInfo = "UNIX";
        } else if (agent.indexOf("Linux") > 0){
            systenInfo = "Linux";
        } else if (agent.indexOf("SunOS") > 0){
            systenInfo = "SunOS";
        }
        return systenInfo;
    }
    /**
     * 获取代理信息
     * @param request
     * @return
     */
    public static UserAgent getUserAgent(HttpServletRequest request) {
        String agent=request.getHeader("User-Agent");
        return getUserAgent(agent);
    }
    /**
     * 获取代理信息
     * @param agent
     * @return
     */
    public static UserAgent getUserAgent(String agent) {
        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
        return userAgent;
    }
    /**
     * 获取手机型号
     * @param request
     * @return
     */
    public static String getMobileModel(HttpServletRequest request) {
        String agent=request.getHeader("User-Agent");
        return getMobileModel(agent);
    }
    /**
     * 获取手机型号
     * @param agent
     * @return
     */
    public static String getMobileModel(String agent) {
        if(agent.indexOf("iPhone") != -1){
            return "iPhone iPhone";
        }
        Pattern pattern = Pattern.compile(";\\s?(\\S*?\\s?\\S*?)\\s?(Build)?/");
        Matcher matcher = pattern.matcher(agent);
        String model = null;
        if (matcher.find()) {
            model = matcher.group(1).trim();
            return model;
        }
        return "";
    }
    /**
     * 根据请求获取客户端信息
     * @param request
     * @return
     */
    public static ClinetRequestInfo getClinetRequestInfo(HttpServletRequest request) {
        UserAgent userAgent = getUserAgent(request);
       ClinetRequestInfo clinetRequestInfo = getClinetRequestInfo(userAgent);
        String deviceType = clinetRequestInfo.getDeviceType();
        if(StringUtils.isNotNull(deviceType) && deviceType.equals(DeviceType.MOBILE.getName())) {
            String mobileModel = getMobileModel(request);
            String[] mobileInfo = mobileModel.split("\\s+");
            clinetRequestInfo.setMobileBrand(mobileInfo[0]);
            clinetRequestInfo.setMobileModel(mobileInfo[1]);
        }
        try {
           IPDetail ipInfo = getIPDetailInfo(request);
            clinetRequestInfo.setIpInfo(ipInfo);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        return clinetRequestInfo;

    }
    /**
     * 获取客户端信息
     * @param agent
     * @param ip
     * @return
     */
    public static ClinetRequestInfo getClinetRequestInfo(String agent, String ip) {
        if(StringUtils.isNull(agent)) return null;
        UserAgent userAgent = getUserAgent(agent);
      ClinetRequestInfo clinetRequestInfo = getClinetRequestInfo(userAgent);
        String deviceType = clinetRequestInfo.getDeviceType();
        if(StringUtils.isNotNull(deviceType) && deviceType.equals(DeviceType.MOBILE.getName())) {
            String mobileModel = getMobileModel(agent);
            String[] mobileInfo = mobileModel.split("\\s+");
            clinetRequestInfo.setMobileBrand(mobileInfo[0]);
            clinetRequestInfo.setMobileModel(mobileInfo[1]);
        }
        try {
          IPDetail ipInfo = getIpDetailInfo(ip);
            clinetRequestInfo.setIpInfo(ipInfo);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        return clinetRequestInfo;
    }

    /**
     * 将IPv4地址转换成字节
     * @param text
     * @return
     */
    public static byte[] textToNumericFormatV4(String text)
    {
        if (text.length() == 0)
        {
            return null;
        }

        byte[] bytes = new byte[4];
        String[] elements = text.split("\\.", -1);
        try
        {
            long l;
            int i;
            switch (elements.length)
            {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L))
                        return null;
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L))
                        return null;
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L))
                        return null;
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < 2; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L))
                            return null;
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L))
                        return null;
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L))
                            return null;
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        }
        catch (NumberFormatException e)
        {
            return null;
        }
        return bytes;
    }

    private static boolean internalIp(byte[] addr)
    {
        if (null==addr || addr.length < 2)
        {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0)
        {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4)
                {
                    return true;
                }
            case SECTION_5:
                switch (b1)
                {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }

    /**
     * 根据IP地址获取真实地址
     * @param urlStr
     * @return
     */
    private static IPDetail getJsonContentForIPAddress(String urlStr) {
        try
        {// 获取HttpURLConnection连接对象
            URL url = new URL(urlStr);
            HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
            // 设置连接属性
            httpConn.setConnectTimeout(3000);
            httpConn.setDoInput(true);
            httpConn.setRequestMethod("GET");
            // 获取相应码
            int respCode = httpConn.getResponseCode();
            if (respCode == 200)
            {
                String jsonStr =  ConvertStream2Json(httpConn.getInputStream());
                log.info(jsonStr);
                ObjectMapper objectMapper = new ObjectMapper();
                JsonNode rootNode = objectMapper.readTree(jsonStr);
                String code = rootNode.get("code").toString();
                if("0".equals(code)) {
                    String data = rootNode.get("data").toString();
                   IPDetail ipDetail = JsonUtils.fromJson(data, IPDetail.class);
                    return ipDetail;
                }

            }
        }
        catch (MalformedURLException e)
        {
            log.error(e.getMessage(),e);
        }
        catch (IOException e)
        {
            log.error(e.getMessage(),e);
        }
        return null;
    }
    /**
     * 获取响应结果
     * @param inputStream
     * @return
     */
    private static String ConvertStream2Json(InputStream inputStream) {
        String jsonStr = "";
        // ByteArrayOutputStream相当于内存输出流
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        // 将输入流转移到内存输出流中
        try
        {
            while ((len = inputStream.read(buffer, 0, buffer.length)) != -1)
            {
                out.write(buffer, 0, len);
            }
            // 将内存流转换为字符串
            jsonStr = new String(out.toByteArray());
        }
        catch (IOException e)
        {
            log.error(e.getMessage(),e);
        }
        return jsonStr;
    }
    /**
     * 获取客户端信息
     * @param //clinetRequestInfo
     * @param userAgent
     * @return
     */
    private static ClinetRequestInfo getClinetRequestInfo(UserAgent userAgent) {
      ClinetRequestInfo clinetRequestInfo = new ClinetRequestInfo();
        if(null != userAgent) {
            clinetRequestInfo.setBrowserInfo(userAgent.getBrowser());
            clinetRequestInfo.setBrowserVersion(userAgent.getBrowserVersion().getVersion());
            clinetRequestInfo.setOsInfo(userAgent.getOperatingSystem());
            clinetRequestInfo.setDeviceType(userAgent.getOperatingSystem().getDeviceType().getName());
        }
        return clinetRequestInfo;
    }

    /**
     * 判断是否为ajax请求
     * @param request
     * @return
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        if (request.getHeader("accept").indexOf("application/json") > -1
                || (request.getHeader("X-Requested-With") != null && request.getHeader("X-Requested-With").equals(
                "XMLHttpRequest"))) {
            return true;
        }
        return false;
    }
}
